
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Diagnostics;

using Swaf;
using Swaf.BizRule;
using Swaf.Container;
using Swaf.Security;

namespace Swaf.Security
{
	/// <summary>
	/// The SecurityRuleDecorator class implements the IRuleDecorator interface and is 
	/// registered in the RuleDecorators classfactory in the SecurityFeature file.  
	/// This decorator is automatically added to all business rules during startup 
	/// configuration unless the rule definition already has it defined.  To decorate 
	/// a rule to override the default behavior of security, add the security attribute 
	/// to the rule definition.  Valid values are true (the default if the decorator is 
	/// not explicitly declared), false for no security or global to use the default 
	/// security principal on the global application object.
	/// </summary>
	public class DynSecurityRuleDecorator : IRuleDecorator
	{
		protected string m_name;
		protected string[] m_resourceNameParts;
		protected int m_paramRef = -1;
		protected bool m_applied = true;
        protected Privileges m_minPrivLevel = Privileges.Execute;
        protected bool m_appliedSecurityLevel = false;

		/// <summary>
		/// Constructor for this decorator
		/// </summary>
		/// <param name="name"></param>
		/// <param name="val"></param>
		/// <param name="extraInfo"></param>
		public DynSecurityRuleDecorator(string name, string val, object extraInfo)
		{
			m_name = name;
			if (val == null || val.Length == 0)
				throw new BaseSecurityException("Invalid to not specify a resource name in the security privilege rule decorator");

            BasicRuleInfo rinfo = extraInfo as BasicRuleInfo;
            string lowerVal = val.ToLower();
			if (lowerVal == "enabled")
			{
				m_resourceNameParts = new string[] { "BizRules." + rinfo.Name };
			}
			else if (lowerVal == "disabled")
				m_applied = false;
			else
			{
                int paramRef = val.IndexOf('{');
				if (paramRef == -1)
					m_resourceNameParts = new string[] { val };
				else
				{
					int paramEndRef = val.IndexOf('}');
					m_resourceNameParts = new string[3];
					m_resourceNameParts[0] = val.Substring(0, paramRef);
					m_resourceNameParts[2] = val.Substring(paramEndRef + 1);
					string paramRefStr = val.Substring(paramRef + 1, paramEndRef - paramRef - 1);
					int dotIndex = paramRefStr.IndexOf('.');
					if (dotIndex == -1)
					{
						m_paramRef = Int32.Parse(paramRefStr);
						m_resourceNameParts[1] = null;
					}
					else
					{
						string refPart1 = paramRefStr.Substring(0, dotIndex);
						m_paramRef = Int32.Parse(refPart1);
						m_resourceNameParts[1] = paramRefStr.Substring(dotIndex + 1);
					}
				}
			}
        }
		#region IRuleDecorator Members

		/// <summary>
		/// Called by the QueueElement prior to rule execution.  This method actuall implements the
		/// security call.  It performs a security assertion against the security principal (if enabled)
		/// using the Privileges.Execute enum as the minimum required privilige. 
		/// </summary>
		/// <param name="info"></param>
		/// <returns></returns>
		public bool preRuleExecution(IRunningRule info)
		{
			if (m_applied)
			{
				ISecurityPrincipal principal = Application.currentApp.securityMgr.currentPrincipal;
				Debug.Assert(principal != null);

				string resourceName = null;
				if (m_resourceNameParts.Length == 1)
					resourceName = m_resourceNameParts[0];
				else if (info.inputParams is object[])
				{
					object[] inputParams = (object[])info.inputParams;
					Debug.Assert(m_paramRef < inputParams.Length);
					object inputParamRef = inputParams[m_paramRef +2];
					string extrVal = null;
					if (m_resourceNameParts[1] != null)
						extrVal = ((INameAddressSupport)inputParamRef).get(m_resourceNameParts[1]).ToString();
					else
						extrVal = inputParamRef.ToString();
					resourceName = m_resourceNameParts[0] + extrVal + m_resourceNameParts[2];
				}
				Debug.Assert(resourceName != null);
                if (resourceName != null)
                {
                    if (!m_appliedSecurityLevel)
                    {
                        m_appliedSecurityLevel = true;
                        if (info.Info.Decorators.Contains("securityLevel"))
                            m_minPrivLevel = (Privileges)Enum.Parse(typeof(Privileges), ((IDecorator)info.Info.Decorators["securityLevel"]).Value, true);
                    }
                    principal.assert(resourceName, m_minPrivLevel);
                }
			}
			return true;
		}

		/// <summary>
		/// Not implemented.
		/// </summary>
		public void postRuleExecution(IRunningRule info, ref object ruleReturn, ref Exception ruleException)
		{
			//Intentionally left blank
		}

		#endregion

		#region IDecorator Members

		/// <summary>
		/// <see cref="IDecorator.decorateInstance"/>
		/// </summary>
		public object decorateInstance(object theInst) { return theInst; }

		/// <summary>
		/// Returns the value specified on the security bizrule decorator which should
		/// be a valid resource name to the ISecurityPrincipal
		/// </summary>
		public string Value {get{ return Cmn.join("",m_resourceNameParts); }}

		/// <summary>
		/// <see cref="IDecorator.Name"/>
		/// </summary>
		public string Name {get{ return m_name;	}}

		/// <summary>
		/// <see cref="IDecorator.IsStandin"/>
		/// </summary>
		public bool IsStandin {get{ return true; }}

		#endregion
	}
}
